package com.sh.data.engine.common.util;

import org.apache.commons.lang3.tuple.ImmutablePair;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

public class FlinkTaskScheduleCycleUtil {

    public static void main(String[] args) throws ParseException {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        SimpleDateFormat format2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        List<ImmutablePair<Date, Date>> effectiveDateStartEndPairList = new ArrayList<>();

        //    effectiveDateStartEndPairList.add(
        //        new ImmutablePair<>(format.parse("2021-06-10"), format.parse("2021-06-12")));
        //
        //    effectiveDateStartEndPairList.add(
        //        new ImmutablePair<>(format.parse("2020-07-15"), format.parse("2020-07-16")));

        effectiveDateStartEndPairList.add(
            new ImmutablePair<>(
                format.parse("2021-06-21 00:00:00"), format.parse("2021-06-28 23:59:59")));

        int effectDayType = 1;

        int startTimeHour = 8;
        int startTimeMin = 0;
        int endTimeHour = 8;
        int endTimeMin = 0;

        final Date nextTime =
            getNextStartTime(
                effectDayType, effectiveDateStartEndPairList, startTimeHour, startTimeMin, false);

        System.out.println(nextTime == null ? "无开始时间" : format2.format(nextTime));

        final Date end =
            getNextStopTime(
                effectDayType,
                effectiveDateStartEndPairList,
                nextTime,
                startTimeHour,
                startTimeMin,
                endTimeHour,
                endTimeMin);

        System.out.println(end == null ? "无结束时间" : format2.format(end));
    }

    /**
     * 获取下一次开始时间
     *
     * @param effectDayType
     * @param effectiveDateStartEndPairList
     * @param startTimeHour
     * @param startTimeMin
     * @return
     */
    public static Date getNextStartTime(
        Integer effectDayType,
        List<ImmutablePair<Date, Date>> effectiveDateStartEndPairList,
        int startTimeHour,
        int startTimeMin,
        boolean includeToday) {

        Calendar nextTime = Calendar.getInstance();

        nextTime.setTime(new Date());
        nextTime.set(Calendar.HOUR_OF_DAY, startTimeHour);
        nextTime.set(Calendar.MINUTE, startTimeMin);
        nextTime.set(Calendar.SECOND, 0);
        nextTime.set(Calendar.MILLISECOND, 0);

        if (!includeToday) {
            nextTime.add(Calendar.DATE, 1);
        }

        effectiveDateStartEndPairList.sort((o1, o2) -> o1.getRight().compareTo(o1.getRight()));

        final Date maxEndTime =
            effectiveDateStartEndPairList.get(effectiveDateStartEndPairList.size() - 1).getRight();

        while (true) {
            final Date nextTimeTime = nextTime.getTime();

            if (nextTimeTime.compareTo(maxEndTime) > 0) {
                return null;
            }

            final boolean inRange = inRange(nextTimeTime, effectiveDateStartEndPairList, effectDayType);

            if (inRange) {
                return nextTimeTime;
            }

            nextTime.add(Calendar.DATE, 1);
        }
    }

    /**
     * 获取下一次结束时间
     *
     * @param effectDayType
     * @param effectiveDateStartEndPairList
     * @param startTimeHour
     * @param startTimeMin
     * @param endTimeHour
     * @param endTimeMin
     * @return
     */
    public static Date getNextStopTime(
        Integer effectDayType,
        List<ImmutablePair<Date, Date>> effectiveDateStartEndPairList,
        Date nextStartTime,
        int startTimeHour,
        int startTimeMin,
        int endTimeHour,
        int endTimeMin) {

        if (null == nextStartTime) {
            return null;
        }

        Calendar nextTime = Calendar.getInstance();

        nextTime.setTime(nextStartTime);
        nextTime.set(Calendar.HOUR_OF_DAY, endTimeHour);
        nextTime.set(Calendar.MINUTE, endTimeMin);
        nextTime.set(Calendar.SECOND, 0);
        nextTime.set(Calendar.MILLISECOND, 0);

        // 开始时间大于结束时间,结束时间加1天,判断加1天后是否在有效期范围内
        if (startTimeHour > endTimeHour
            || (startTimeHour == endTimeHour && startTimeMin > endTimeMin)) {
            nextTime.add(Calendar.DATE, 1);
            final boolean inRange =
                inRange(nextTime.getTime(), effectiveDateStartEndPairList, effectDayType);
            if (inRange) {
                return nextTime.getTime();
            } else {
                nextTime.add(Calendar.DATE, -1);
                nextTime.set(Calendar.HOUR_OF_DAY, 23);
                nextTime.set(Calendar.MINUTE, 59);
                nextTime.set(Calendar.SECOND, 59);
                nextTime.set(Calendar.MILLISECOND, 999);
                return nextTime.getTime();
            }
        }

        // 开始时间等于结束时间，结束时间为当前时间段内的末尾
        if (startTimeHour == endTimeHour && startTimeMin == endTimeMin) {
            while (inRange(nextTime.getTime(), effectiveDateStartEndPairList, effectDayType)) {
                nextTime.add(Calendar.DATE, 1);
            }
            nextTime.add(Calendar.DATE, -1);
            if (nextStartTime.compareTo(nextTime.getTime()) == 0) {
                nextTime.set(Calendar.HOUR_OF_DAY, 23);
                nextTime.set(Calendar.MINUTE, 59);
                nextTime.set(Calendar.SECOND, 59);
                nextTime.set(Calendar.MILLISECOND, 999);
            }
            return nextTime.getTime();
        }

        return nextTime.getTime();
    }

    /**
     * 判断给定的时间是否在有效范围内
     *
     * @param date
     * @param effectiveDateStartEndPairList
     * @param effectDayType
     * @return
     */
    private static boolean inRange(
        Date date,
        List<ImmutablePair<Date, Date>> effectiveDateStartEndPairList,
        Integer effectDayType) {
        final DayOfWeek dayOfWeek = date2LocalDate(date).getDayOfWeek();
        if (effectDayType != null && effectDayType == 1) {
            // 周六日
            if (dayOfWeek.equals(DayOfWeek.SATURDAY) || dayOfWeek.equals(DayOfWeek.SUNDAY)) {
                return false;
            }
        }

        for (ImmutablePair<Date, Date> dateDateImmutablePair : effectiveDateStartEndPairList) {
            final Date startTime = dateDateImmutablePair.getLeft();
            final Date endTime = dateDateImmutablePair.getRight();

            if (startTime.compareTo(date) <= 0 && endTime.compareTo(date) >= 0) {
                return true;
            }
        }

        return false;
    }

    private static LocalDate date2LocalDate(Date date) {
        if (null == date) {
            return null;
        }
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    }
}
